package com.skywolf.chem.match.handle;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.skywolf.chem.common.config.data.DataSource;
import com.skywolf.chem.common.config.data.DataSourceNames;
import com.skywolf.chem.common.sysconfig.exception.BizException;
import com.skywolf.chem.match.common.constants.MatchConstants;
import com.skywolf.chem.match.entity.ApplyUserRecord;
import com.skywolf.chem.match.entity.GroupInfo;
import com.skywolf.chem.match.service.IGroupInfoService;
import com.skywolf.chem.match.utils.RedisUtils;
import com.skywolf.chem.match.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 组别库存处理器
 * @Author sj
 * Date on 2021/10/14  9:45
 * @Version 1.0
 */
@Component
@AllArgsConstructor
@Slf4j
public class MatchGroupStockHandle {


    private final RedisUtils redisUtils;

    private final ThreadPoolExecutor threadPoolExecutor;

    private final RedisTemplate<String, Object> redisTemplate;

    private final RedissonClient redissonClient;

    private final IGroupInfoService groupInfoService;


    /**
     * Cache-Aside pattern 缓存、数据库读写模式
     * 1. 读取数据，先读缓存，没有就去读数据库，然后将结果写入缓存
     * 2. 写入数据，先更新数据库，再删除缓存
     *
     * @param groupId 库存ID
     * @return
     */
    @DataSource(DataSourceNames.TWO)
    public Integer getStockById(Long groupId) {
        Integer stock = 0;
        // 读->缓存
        Object cacheVal = redisTemplate.opsForValue().get(MatchConstants.LOCKED_STOCK_PREFIX + groupId);
        if (cacheVal != null) {
            stock = Convert.toInt(cacheVal);
            return stock;
        }

        // 读->数据库
        GroupInfo groupInfo = groupInfoService.getOne(new LambdaQueryWrapper<GroupInfo>()
                .eq(GroupInfo::getId, groupId)
                .select(GroupInfo::getStock));

        if (groupInfo != null) {
            stock = groupInfo.getStock();
            // 写->缓存
            redisTemplate.opsForValue().set(MatchConstants.LOCKED_STOCK_PREFIX + groupId, String.valueOf(stock));
        }

        return stock;
    }

    /**
     * 支付成功根据订单号扣减商品库存
     *
     * @param orderToken
     * @return
     */
    @DataSource(DataSourceNames.TWO)
    public boolean deductStock(String orderToken) {
        log.info("=======================支付成功扣减库存=======================");
        Object json = redisTemplate.opsForValue().get(MatchConstants.LOCKED_STOCK_PREFIX + orderToken);
        if (StringUtils.isEmpty(json)) {
            return true;
        }
        List<ApplyUserRecord> list = JSONUtil.toList(json.toString(), ApplyUserRecord.class);
        boolean result = groupInfoService.update(new LambdaUpdateWrapper<GroupInfo>()
                .eq(GroupInfo::getId, list.get(0).getMatchGroupId())
                // 扣减库存
                .setSql("stock = stock - " + list.size())
                .setSql("locked_stock = locked_stock - " + list.size())
        );
        if (!result) {
            throw new BizException("扣减库存失败,库存不足");
        }
        // 删除redis中锁定的库存
        redisTemplate.delete(MatchConstants.LOCKED_STOCK_PREFIX + orderToken);
        return true;
    }


    /**
     * 锁定库存
     *
     * @param lockCount
     * @return
     */
    @DataSource(DataSourceNames.TWO)
    public Boolean lockStock(Integer lockCount, Long groupId) {
        // 获取组别分布式锁
        RLock lock = redissonClient.getLock(MatchConstants.LOCK_SKU_PREFIX + groupId);
        lock.lock();
        boolean result = groupInfoService.update(new LambdaUpdateWrapper<GroupInfo>()
                .setSql("locked_stock = locked_stock + " + lockCount)
                .eq(GroupInfo::getId, groupId)
                .apply("stock - locked_stock >= {0}", lockCount));
        lock.unlock();
        return result;
    }

    @DataSource(DataSourceNames.TWO)
    public boolean unlockStock(String orderToken) {
        Object json = redisTemplate.opsForValue().get(MatchConstants.LOCKED_STOCK_PREFIX + orderToken);
        log.info("释放库存信息：{}", json);
        if (null == json) {
            return true;
        }
        List<ApplyUserRecord> list = JSONUtil.toList(json.toString(), ApplyUserRecord.class);
        groupInfoService.update(new LambdaUpdateWrapper<GroupInfo>()
                .eq(GroupInfo::getId, list.get(0).getMatchGroupId())
                .setSql("locked_stock = locked_stock - " + list.size()));
        // 删除redis中锁定的库存
        redisTemplate.delete(MatchConstants.LOCKED_STOCK_PREFIX + orderToken);
        return true;
    }

}
